package com.facebook.swift.codec.metadata;

import java.util.Collection;
import javax.annotation.Nullable;
import javax.annotation.concurrent.Immutable;

import com.google.common.base.Function;
import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.google.common.collect.Collections2;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;

import net.gdface.utils.ClassCommentProvider;

import static com.facebook.swift.codec.metadata.DecoratorThriftServiceMetadata.javadocCommentProviderFactory;

/**
 * {@link ThriftStructMetadata}的代理类<br>
 * 重载所有{@link ThriftFieldMetadata}相关方法
 * @author guyadong
 *
 */
@Immutable
public class DecoratorThriftStructMetadata extends ThriftStructMetadata {
	/**
	 * 类型名称转换实例<br>
	 * 输入原始类型名<br>
	 * 输出转换后的类型名
	 */
	public static Function<String, String> structNameTransformer = null;
	/**
	 * 字段名过滤器
	 */
	public static FieldNameFilters fieldNameFilters = null;
    /** {@link DecoratorThriftStructMetadata}缓存对象,
     * 保存每个{@link ThriftStructMetadata}对应的{@link DecoratorThriftStructMetadata}实例 
     */
    private static final LoadingCache<ThriftStructMetadata,DecoratorThriftStructMetadata> 
    	STRUCTS_CACHE = 
    		CacheBuilder.newBuilder().build(
    				new CacheLoader<ThriftStructMetadata,DecoratorThriftStructMetadata>(){
						@Override
						public DecoratorThriftStructMetadata load(ThriftStructMetadata key) throws Exception {
							return new DecoratorThriftStructMetadata(key);
						}});
    /**  将{@link ThriftStructMetadata}转换为 {@link DecoratorThriftStructMetadata}对象 */
    public static final Function<ThriftStructMetadata,ThriftStructMetadata> 
    	STRUCT_TRANSFORMER = new Function<ThriftStructMetadata,ThriftStructMetadata>(){
    		@Nullable
			@Override
			public ThriftStructMetadata apply(@Nullable ThriftStructMetadata input) {
				return null == input || input instanceof DecoratorThriftStructMetadata
						? input
						: STRUCTS_CACHE.getUnchecked(input);
			}};
	    /** 
	     * {@link DecoratorThriftFieldMetadata}缓存对象,
	     * 保存每个{@link ThriftFieldMetadata}对应的{@link DecoratorThriftFieldMetadata}实例 
	     */
	    private final LoadingCache<ThriftFieldMetadata,DecoratorThriftFieldMetadata> 
	    	FIELDS_CACHE = 
	    		CacheBuilder.newBuilder().build(
	    				new CacheLoader<ThriftFieldMetadata,DecoratorThriftFieldMetadata>(){
							@Override
							public DecoratorThriftFieldMetadata load(ThriftFieldMetadata key) throws Exception {
								return new DecoratorThriftFieldMetadata(key).setJavadocCommentProvider(javadocCommentProvider);
							}});
	    /**  将{@link ThriftFieldMetadata}转换为 {@link DecoratorThriftFieldMetadata}对象 */
		private  final Function<ThriftFieldMetadata,ThriftFieldMetadata> 
			FIELD_TRANSFORMER = 
				new Function<ThriftFieldMetadata,ThriftFieldMetadata>(){
					@Nullable
					@Override
					public ThriftFieldMetadata apply(@Nullable ThriftFieldMetadata input) {
					    return null == input || input instanceof DecoratorThriftFieldMetadata  
					    		? input 
					    		: FIELDS_CACHE.getUnchecked(input);
					}};
	private final ClassCommentProvider javadocCommentProvider ;
	private volatile ImmutableList<String> documentation;
	private volatile Boolean hasPointerMember;
	private final Predicate<ThriftFieldMetadata> fieldsMetadataFilter ;
	private DecoratorThriftStructMetadata(ThriftStructMetadata input){
		super(input.getStructName(), 
				input.getStructType(), 
				input.getBuilderType(), 
				input.getMetadataType(), 
				input.getBuilderMethod(), 
				input.getDocumentation(), 
				ImmutableList.copyOf(input.getFields()),
				input.getConstructorInjection(), 
				input.getMethodInjections());
		javadocCommentProvider = javadocCommentProviderFactory != null 
				? javadocCommentProviderFactory.apply(getStructClass())
				: null;

		fieldsMetadataFilter  = fieldNameFilters != null ? new Predicate<ThriftFieldMetadata>() {
					final Predicate<String>filter = fieldNameFilters.filterOf(getStructClass());
					@Override
					public boolean apply(ThriftFieldMetadata input) {			
						return filter.apply(input.getName());
					}
				} : Predicates.<ThriftFieldMetadata>alwaysTrue();
	}
	@Override
	public ThriftFieldMetadata getField(int id) {
		return FIELD_TRANSFORMER.apply(super.getField(id));
	}

	@Override
	public Collection<ThriftFieldMetadata> getFields() {
		Collection<ThriftFieldMetadata> filtered = Collections2.filter(super.getFields(), fieldsMetadataFilter);
		return Collections2.transform(filtered, FIELD_TRANSFORMER);
	}

	@Override
	public Collection<ThriftFieldMetadata> getFields(FieldKind type) {
		return Collections2.transform(super.getFields(type), FIELD_TRANSFORMER);
	}
	
	@Override
	public ImmutableList<String> getDocumentation() {
		// double checking
		if(documentation == null){
			synchronized (this) {
				if(documentation == null){
					documentation = super.getDocumentation();
					if( javadocCommentProvider != null){
						if(documentation == null || documentation.isEmpty()){
							documentation = javadocCommentProvider.commentOfClass();
						}
					}
				}
			}
		}
		return documentation;
	}
	@Override
	public String getStructName() {
		return structNameTransformer != null 
				? structNameTransformer.apply(super.getStructName()) 
				: super.getStructName();
	}
	public ThriftFieldMetadata getField(final String name) {
		return Iterables.tryFind(getFields(),new Predicate<ThriftFieldMetadata>() {

			@Override
			public boolean apply(ThriftFieldMetadata input) {
				return input.getName().equals(name);
			}
		}).orNull();
	}
	public boolean isHasPointerMember(){
		if(hasPointerMember == null){
			synchronized (this) {
				if(hasPointerMember == null){
					hasPointerMember = 
					Iterables.tryFind(getFields(), new Predicate<ThriftFieldMetadata>(){

						@Override
						public boolean apply(ThriftFieldMetadata input) {
							return ((DecoratorThriftFieldMetadata)input).isPointer();
						}}).isPresent();
				}
			}
		}
		return hasPointerMember;
	}
}
